#ifndef XGE_EVENT_BASE_H_
#define XGE_EVENT_BASE_H_
#include "xge_common_config.h"
#include "xsmart_ptr.h"
#include "xtimer.h"
#include "xdata_structure.h"
#include "xbase_common.h"
#include "xallocator.h"


namespace xge{
  class MainLoop;
  class XGameEngine;
namespace event{
  using xge_internal::IntrinsicPointer;
  using xge_internal::Timer;
  
  class EventManager;
  class TriggerEvent;
  class EventIdentity;

  // This class will provide the event interface for dealing with the event!
  // A event is kind of class which will deal some logical things ,like
  // an NPC moving operation or some other things ! A event should be 
  // triggered by something , like when the time clock reached or the 
  // frame difference reaches , anything can be done by reaching the event.
  // What is more , event has been forced to RUN in the main thread , if you
  // are asking for the asynchronizing interface , you should ask for the task
  // interface , which provide the code executing on a different thread !
  // In order to make the event easier to use and more efficient , we separate
  // the event interface by grouping them . 


  // Event :
  // This event has been guaranteed to be calling in every frame , so if you are
  // asking this kind of event , you just derive from it , and it is ok as you 
  // see !

  class Event : public IntrinsicPointer< Event > {
  public:
    // Please override this function , and this function will be called
    // in every loop !
    virtual void OnLoop( const Timer::tick_t& time_diff ) = 0;
    virtual bool Reset() = 0;

    Event( size_t life_time = 0 ) : id_(NULL) , life_time_ (life_time) {}
    virtual ~Event() {}
  public:
    // This function will tell the user that weather this event has
    // been pushed into the event manager internal list or not .
    bool binded() const {
      return id_ != NULL;
    }
  protected:
    // This function will testing 
    void set_life_time( int time ) {
      life_time_ = time;
    }
    int life_time() const {
      return life_time_;
    }
  private:
    // This class is only a indicator for implementing the O(1) remove
    // operation by event manager !
    EventIdentity* id_;
    int life_time_;
    friend class EventManager;

  };


  // Trigger event is another very common event , this event will be triggered
  // by a certain trigger and than on event 
  
  // Trigger is a class to tell the EventManager that weather a certain event
  // should be triggering when the certain trigger is on ! A trigger can be
  // binded to more than one event !
  class Trigger : public Event {
  public:
    // The trigger will be called in every frame , typically 
    // the IsOn function will testing the time elapsed or check
    // back-thread working stuff , if the certain target message
    // not happens , it will simply return true which means the 
    // event can happens now , otherwise it return failed which
    // means the event should not happen ;if certain exception 
    // happens , the trigger provide you another way to reduce 
    // the stuff here , you can return exception and the event
    // manager will record a PANIC information and remove the
    // event in order to avoid the resource wasting .
    virtual const TripleResult IsOn( const Timer::tick_t& time_diff ) = 0;
    // When the event has finished its task , the event will typically
    // be removed from the event list , however , the event can be
    // registered as permanent event , so the trigger should be reset
    // to the right status .However the base class Reset should be called
    // at the first of Derived Trigger Class!!!
    virtual bool Reset() ;

    Trigger();
    virtual ~Trigger() {
      ClearTriggerEvent();
    }
  public:
    // Add a trigger event into this trigger list , this will make the
    // target event be binded by this trigger and not available for others
    bool AddTriggerEvent( TriggerEvent* , bool force = true );
    // Remove a trigger event from this triggering list . The target trigger
    // event should be in this trigger !
    bool RemoveTriggerEvent( TriggerEvent* event );
    // Size of the trigger list
    size_t NumberOfTriggerEvent() const {
      return trigger_event_list_.size();
    }
    // Is the trigger list empty 
    bool IsTriggerListEmpty() const {
      return trigger_event_list_.empty();
    }

    void ClearTriggerEvent() ;

  private: 
    // This function will be called by the EventManager
    virtual void OnLoop( const Timer::tick_t& time_diff );
    typedef xge::list< TriggerEvent* >::List TriggerEventList ;
  public:
    // A tag typedef which make the trigger event be deleted 
    // in O(1) complexity 
    typedef TriggerEventList::iterator TriggerEventIdentity;
  private:
    // This will do the removing operation internally 
    TriggerEventIdentity RemoveTriggerEventInternal( TriggerEvent* );
  private:

    typedef xge::list< TriggerEvent* >::List TriggerEventList ;
    TriggerEventList trigger_event_list_;
    // This value will be assigned that how many loops this
    // trigger has not been triggered !
    size_t free_loop_;
    Timer::tick_t elapsed_time_;
    friend class EventManager;
  };

  // A trigger event is a event will be triggered by the trigger 
  // It can only bind to only ONE trigger .Once it has been binded
  // it will be sent into the waiting list to wait for the trigger
  // happens 
  class TriggerEvent : public IntrinsicPointer< TriggerEvent >  {
  public:
    // This function will be called when the target trigger is working
    virtual void OnEvent( const Timer::tick_t& time , size_t frame_diff ) = 0;
    // If this TriggerEvent has been binded into a trigger , this event 
    // will receive a trigger
    Trigger* GetTrigger() const {
      return trigger_;
    }
    // Set the trigger life time 
    // You specify the times you want for this event , if you indicate that
    // parameter as negative number , the event will be working forever , 
    // otherwise once the event calling times reach , the event will be removed
    // from the trigger event list automatically ! Like :
    // set_life_time(-1); 
    // set_life_time(1) ; 
    // set_life_time(1000);
    void set_life_time( int times ) {
      life_time_ = times;
    }

    int life_time() const {
      return life_time_;
    }

    // Alive will check weather this event has been removed from the calling list 
    bool alive() const {
      return trigger_ != NULL ;
    }

    virtual ~TriggerEvent() {}
  protected:
    // Reset function when this event has been finished , if it needs to run again
    // this function will be called
    virtual bool Reset() = 0;
  private:
    // The binded trigger of this trigger event
    Trigger* trigger_;
    // The id of this trigger event 
    Trigger::TriggerEventIdentity id_;
    // The life time tag
    int life_time_;
    friend class Trigger;
  };

  // Event manager provide you way to manage the event calling 
  // you can add a event into the event manager or remove a 
  // event from the event manager .
  // In fact , the event manager will be scheduled in the MainLoop
  // , however , we do not control the loop iteration speed . However
  // we can also take advantage of the EventManager to schedule the 
  // timer here ! 

  class EventManager : Allocator {
  public:
    // You just register the interesting event into this event manager
    // The event manager will not classify the Event derived class or
    // the Trigger like class , since both are the event sub class ,the
    // event manager will only calling the corresponding function to do
    // the iterating .
    // If you want to register a TriggerEvent , you just do it like this
    // Trigger* trigger = new MyTrigger();
    // trigger->AddTriggerEvent( new MyTriggerEvent() );
    // AddEvent( trigger ); 
    bool AddEvent( Event * );
    // Remove a certain event inside of this event manager , this will always
    // take O(1) complexity!
    bool RemoveEvent  ( Event* event) {
      // testing if this event has been added or not
      if( event->id_ == NULL )
        return false;
      RemoveEventInternal(event);
      return true;
    }
    // Get the number of the event 
    size_t NumberOfEvent () const {
      return event_list_.size();
    }
    bool IsEventListEmpty() const {
      return event_list_.empty();
    }
    // Clear the event
    void ClearEvent();

    // Set the speed of the loop

    void SetFPS( size_t fps ) {
      loop_speed_ = fps ; 
      time_ = (Timer::tick_t)(1000/fps);
      cur_time_ = 0;
    }

    size_t GetFPS() const {
      return loop_speed_;
    }


  public:

  private:
    // This private function will be called in every frame by the MainLoop class
    void Loop( const Timer::tick_t& time );
  private:
    // We will wrap a structure to store the event list 
    struct EventWrapper {
      Event* event ; // The pointer to the event itself 
      typedef xge::list< EventWrapper >::List EventList;
      typedef EventList::iterator RealEventIdentity;
      RealEventIdentity id ;

      EventWrapper(Event* event);
      EventWrapper( const EventWrapper& wrapper );
      ~EventWrapper();

    };
    // The internal data structure for storing the event 
    // We use list as holder for the event , and using the
    // iterator as the tag for identify the node position
    // which allows us to remove the certain event in O(1)
    typedef EventWrapper::EventList EventList;
    EventList event_list_;

    // This internal function will perform the event remove
    // and return the list position of the stl::list which
    // is very useful for iterating the list !
    typedef EventWrapper::RealEventIdentity RealEventIdentity;
    RealEventIdentity RemoveEventInternal( Event* event );

    // CTor and DTor will be private function , since this 
    // class can only be created by XGameEngine 

    EventManager() {
      SetFPS(60);
    }

    ~EventManager() {
      ClearEvent();
    }

    // The following stuff is used to constrain the loop speed 
    size_t loop_speed_;
    Timer::tick_t cur_time_;
    Timer::tick_t time_;

    friend class XGameEngine;
    friend class MainLoop;
  };



}// namespace event
}// namespace xge



#endif// XGE_EVENT_BASE_H_